// avrusbnboot.cpp
//
// ver date       who  comment
//----------------------------------------------------------------------------
// v01 2008-02-21 bs   created
// v02 2008-02-21 kh   extended
//
// $Log:$
//

#pragma ident "$Id:$"

#include <stdio.h>
#include <usb.h>

#define STARTAPP  0x01
#define WRITEPAGE 0x02

//char code[64]={0x0f, 0xef, 0x07, 0xbb,  0x0c, 0xef, 0x08, 0xbb,  0xff ,0xcf};

static char	epn = 2;

static int easyAVR_FlashBin (struct usb_dev_handle *usb_handle, char *file)
{
	// get page size
	// get flash size

	// send every page in a usb package
	// 1. header ( action, page number)
	// 2. data   

	// if you have got a controller with blocksize greater than 64
	// work with the easyavr like you have an 64 page size flash
	// the controller manages the rest

	//printf("send %s to easyavr\r\n",file);

	// open bin file
	FILE *fd = fopen (file, "rb");
	if (!fd)
	{
		fprintf (stderr, "Unable to open file %s, ignoring.\n", file);
		return -1;
	}

	int     page = 0;

	while (!feof (fd))
	{
		char    buf[256];
		memset (buf, 255, sizeof(buf));
		size_t n = fread (buf, 1, sizeof(buf), fd);

		if (n > 0)
		{
			int	 rc;
			char cmd[3];

			//printf("send package\n");
			// command message
			cmd[0] = WRITEPAGE;
			cmd[1] = (char) page;			// page number
			cmd[2] = (char) (page >> 8);	// page number
			rc = usb_bulk_write (usb_handle, epn, cmd, sizeof(cmd), 5000);
			if (rc < 0)
			{
				printf ("error usb_bulk_write 1: %d: %s\n", rc, usb_strerror ());
				return -2;
			}

			// data message 
			rc = usb_bulk_write (usb_handle, epn, buf, sizeof(buf), 2000);
			if (rc < 0)
			{
				printf ("error usb_bulk_write 2: %d: %s\n", rc, usb_strerror ());
				return -3;
			}
			
			// get response
			rc = usb_bulk_read (usb_handle, 0x80 | epn, buf, 4, 6000);
			if (rc < 0)
			{
				printf ("error usb_bulk_read: %d: %s\n", rc, usb_strerror ());
				return -3;
			}
			printf ("page %03X (%04X)\n", *(WORD *) (buf + 2), *(WORD *) buf);
			page++;
		}
	}
	return 0;
}

static int easyAVR_StartApp (struct usb_dev_handle *usb_handle)
{
	char    cmd[1];

	cmd[0] = STARTAPP;
	int rc = usb_bulk_write (usb_handle, epn, cmd,  sizeof(cmd), 5000);
	if (rc < 0)
	{
		printf ("error usb_bulk_write 3: %d: %s\n", rc, usb_strerror ());
		return -2;
	}
	//printf("TX stat=%d\n",stat);
	return 0;
}

#define VID 0x1781
#define PID 0x0c65

struct usb_dev_handle *easyAVR_Open ()
{
//	usb_set_debug (2);
	usb_init ();
	usb_find_busses ();
	usb_find_devices ();

	struct usb_bus *bus = usb_get_busses ();
	for ( ; bus; bus = bus->next)
	{
		struct usb_device *dev;
		for (dev = bus->devices; dev; dev = dev->next)
		{
			if (dev->descriptor.idVendor == VID)
			{
				struct usb_dev_handle *usb_handle;
				//printf ("vendor: %i\n",dev->descriptor.idVendor);
				usb_handle = usb_open (dev);
				return usb_handle;
			}
		}
	}
	return NULL;
}

static int init_dev (struct usb_dev_handle *usb_handle)
{
	if (usb_set_configuration (usb_handle, 1))
	{
		printf ("error usb_set_configuration: %s\n", usb_strerror ());
		return -2;
	}

	if (usb_claim_interface (usb_handle, 0))
	{
		printf ("can't claim interface for given usb device\n");
		return -3;
	}

	if (usb_set_altinterface (usb_handle, 0))
	{
		printf ("can't set altinterface for given usb device\n");
		return -4;
	}

	return 0;
}

static void easyAVR_Close (struct usb_dev_handle *usb_handle)
{
	usb_close (usb_handle);
}


int main (int argc, char **argv)
{
	// start with the destination of your bin file

	struct usb_dev_handle *usb_handle = easyAVR_Open ();
	if (usb_handle == NULL)
		return 1;
	
	if (init_dev (usb_handle) != 0)
	{
		easyAVR_Close (usb_handle);
		return 2;
	}
	
	int rc = 0;
	if (argc == 2)
	{
		char *fni = argv[1];
		rc = easyAVR_FlashBin (usb_handle, fni);
		if (rc == 0)
			printf ("downloaded succesfully: %s\n", fni);
	}

	if (rc == 0)
		easyAVR_StartApp (usb_handle);
	
	easyAVR_Close (usb_handle);
	return 0;
}
